package com.xms.sdk.client;

import com.xms.core.data.Entity;
import com.xms.core.enums.AccessRightValue;
import com.xms.core.enums.OperationStage;
import com.xms.core.enums.OperationType;
import com.xms.core.exception.XmsException;
import com.xms.plugin.service.IEntityPluginExecutor;
import com.xms.schema.core.EntityMask;
import com.xms.schema.entity.Attribute;
import com.xms.schema.extension.AttributeExt;
import com.xms.schema.service.attribute.IAttributeFinderService;
import com.xms.sdk.core.EntityReference;
import com.xms.sdk.core.OptionSetValue;
import com.xms.sdk.core.OwnerObject;
import com.xms.sdk.core.OwnerTypes;
import com.xms.sdk.data.IOrganizationDataProvider;
import com.xms.sdk.event.EntityCreatedEvent;
import com.xms.sdk.event.EntityCreatingEvent;
import com.xms.sdk.service.IDataAssigner;
import com.xms.sdk.service.IDataCreater;
import com.xms.sdk.service.IEntityValidator;
import com.xms.utils.UUIDUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.sql.SQLException;
import java.time.LocalDateTime;
import java.util.List;
import java.util.UUID;

/**
 * <p>
 * DataCreater
 * </p>
 *
 * @author migo
 * @since 2021/9/6
 */
@Service
public class DataCreater extends DataProviderBase implements IDataCreater {
    private IOrganizationDataProvider _organizationDataProvider;
    private IAttributeFinderService _attributeFinderService;
    private IEntityValidator _entityValidator;
    private IDataAssigner _dataAssigner;
    private IEntityPluginExecutor _entityPluginExecutor;

    @Autowired
    public void DataCreater(IOrganizationDataProvider organizationDataProvider
            , IAttributeFinderService attributeFinderService
            , IEntityValidator entityValidator
            , IDataAssigner dataAssigner
            , IEntityPluginExecutor entityPluginExecutor) {
        _organizationDataProvider = organizationDataProvider;
        _attributeFinderService = attributeFinderService;
        _entityValidator = entityValidator;
        _dataAssigner = dataAssigner;
        _entityPluginExecutor = entityPluginExecutor;
    }

    @Override
    public UUID create(Entity entity, boolean ignorePermissions) throws XmsException, SQLException {
        com.xms.schema.entity.Entity entityMetadata = getEntityMetaData(entity.Name);
        List<Attribute> attributeMetadatas = _attributeFinderService.getByEntityId(entityMetadata.getEntityid());
        verifyCreate(entity, entityMetadata, attributeMetadatas, ignorePermissions);
        UUID id = null;
        try {
            internalOnCreate(entity, OperationStage.PreOperation, entityMetadata, attributeMetadatas);
            id = _organizationDataProvider.create(entity);
            if (entityMetadata.getEntitymask() == EntityMask.User.value()) {
                OwnerObject owner = (OwnerObject) entity.getValue("ownerid");
                //assign to other
                if (!owner.OwnerId.equals(this._currentUser.getUser().SystemUserId)) {
                    _dataAssigner.assign(entityMetadata.getEntityid(), id, owner);
                }
            }
            internalOnCreate(entity, OperationStage.PostOperation, entityMetadata, attributeMetadatas);
            publishEvents(entity, OperationStage.PostOperation, entityMetadata, attributeMetadatas);
        } catch (Exception e) {
            onException(new XmsException(e.getMessage()));
        }
        return id;
    }

    @Override
    public UUID create(Entity entity) throws XmsException, SQLException {
        return this.create(entity, false);
    }

    @Override
    public boolean createMany(List<Entity> entities, boolean ignorePermissions) throws XmsException {
        com.xms.schema.entity.Entity entityMetadata = getEntityMetaData(entities.get(0).Name);
        List<Attribute> attributeMetadatas = _attributeFinderService.getByEntityId(entityMetadata.getEntityid());
        boolean success;
        try {
            for (Entity entity : entities) {
                verifyCreate(entity, entityMetadata, attributeMetadatas, ignorePermissions);
                internalOnCreate(entity, OperationStage.PreOperation, entityMetadata, attributeMetadatas);
            }
            success = _organizationDataProvider.createMany(entities);
            if (success) {
                for (Entity item : entities) {
                    internalOnCreate(item, OperationStage.PostOperation, entityMetadata, attributeMetadatas);
                }
            }
        } catch (Exception e) {
            return onException(e);
        }
        if (success) {
            for (Entity item : entities) {
                publishEvents(item, OperationStage.PostOperation, entityMetadata, attributeMetadatas);
            }
        }
        return success;
    }

    @Override
    public boolean createMany(List<Entity> entities) throws XmsException {
        return this.createMany(entities, false);
    }

    private void verifyCreate(Entity entity, com.xms.schema.entity.Entity entityMetadata, List<Attribute> attributeMetadatas, boolean ignorePermissions) throws XmsException, SQLException {
        _entityValidator.verifyValues(entity, entityMetadata, attributeMetadatas);
        if (attributeMetadatas.stream().anyMatch(n -> n.getName().equalsIgnoreCase("createdby"))) {
            entity.setValue("createdby", new EntityReference("systemuser", _currentUser.getUser().SystemUserId));
        }
        if (attributeMetadatas.stream().anyMatch(n -> n.getName().equalsIgnoreCase("ownerid"))) {
            entity.addIfNotContain("ownerid", new OwnerObject(OwnerTypes.SystemUser, _currentUser.getUser().SystemUserId));
        }
        if (attributeMetadatas.stream().anyMatch(n -> n.getName().equalsIgnoreCase("owningbusinessunit"))) {
            entity.setValue("owningbusinessunit", new EntityReference("businessunit", _currentUser.getUser().BusinessUnitId));
        }
        if (!entity.getIdName().equalsIgnoreCase("organizationid") && attributeMetadatas.stream().anyMatch(n -> n.getName().equalsIgnoreCase("organizationid"))) {
            entity.setValue("organizationid", new EntityReference("organization", _currentUser.getUser().OrganizationId));
        }
        if (attributeMetadatas.stream().anyMatch(n -> n.getName().equalsIgnoreCase("createdon"))) {
            entity.setValue("createdon", LocalDateTime.now());
        }
        Attribute primaryAttr = attributeMetadatas.stream().filter(n -> AttributeExt.typeIsPrimaryKey(n)).findFirst().get();
        if (!entity.containsKey(primaryAttr.getName()) || UUIDUtil.isEmpty(entity.getIdValue())) {
            entity.setValue(primaryAttr.getName(), UUIDUtil.get());
        }
        //set attribute default value
        /*attributeMetadatas.stream().filter(n -> StringUtil.notEmpty(n.getDefaultvalue())).forEach(x->{
            if(!x.getName().equalsIgnoreCase("owneridtype"))
                entity.addIfNotContain(x.getName(), x.getDefaultvalue());
        });*/
        if (attributeMetadatas.stream().anyMatch(n -> n.getName().equalsIgnoreCase("statecode"))) {
            entity.addIfNotContain("statecode", true);
        }
        if (attributeMetadatas.stream().anyMatch(n -> n.getName().equalsIgnoreCase("statuscode"))) {
            entity.addIfNotContain("statuscode", new OptionSetValue(0));
        }
        if (!ignorePermissions) {
            verifyEntityPermission(entity, AccessRightValue.Create, entityMetadata);
        }
    }

    private void internalOnCreate(Entity data, OperationStage stage, com.xms.schema.entity.Entity entityMetadata, List<com.xms.schema.entity.Attribute> attributeMetadatas) throws XmsException {
        try {
            _entityPluginExecutor.execute(OperationType.Create, stage, data, entityMetadata, attributeMetadatas);
        }catch(Exception e){
            onException(e);
        }
    }

    // 发布事件
    private void publishEvents(Entity data, OperationStage stage, com.xms.schema.entity.Entity entityMetadata, List<Attribute> attributeMetadatas)
    {
        if (stage.equals(OperationStage.PreOperation)) {
            //event publishing
            _eventPublisher.publish(new EntityCreatingEvent(data, entityMetadata, attributeMetadatas));
        } else if (stage.equals(OperationStage.PostOperation)) {
            //event publishing
            _eventPublisher.publish(new EntityCreatedEvent(data, entityMetadata, attributeMetadatas));
        }
    }
}
